package jp.ne.iforce.android.wifioffload;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.Result;

import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;

import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import static jp.ne.iforce.android.wifioffload.BarCodeActivity.TAG;

/**
 * プレビューの表示とデータの解析を行う
 *
 * @author iforce.ne.jp
 * @version 0.1
 */
class Preview extends SurfaceView implements SurfaceHolder.Callback {

	// オートフォーカスした画像を解析する回数
	private static final int TRY_PREVIEW = 20;

	private BarCodeActivity barCodeActivity;
	private Camera camera;
	private FinderView finderView;
	private SurfaceHolder sh;
	private Rect rect;// 解析エリア
	private Point mResolution;// 画像の縦横ピクセル数
	private Decoder decoder;// バーコードデコーダ
	private ResultDialog mResultDialog;
	private int previewCount;
	private boolean isPreviewing = false;

	/**
	 * プレビュー
	 *
	 * @param Context context(Context), AttributeSet attrs(AttributeSet)
	 */
	public Preview(Context context, AttributeSet attrs) {
		super(context, attrs);
		Log.i(TAG, "new Preview");

		decoder = new Decoder();
		mResultDialog = new ResultDialog(context);
		barCodeActivity = (BarCodeActivity) context;

		sh = getHolder();
		sh.addCallback(this);
		sh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	}

	public void surfaceCreated(SurfaceHolder holder) {
		Log.i(TAG, "surfaceCreated");
		// surfaceが作られると共にカメラを開く
		camera = Camera.open();
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		Log.i(TAG, "surfaceDestroyed");
		if (camera != null && isPreviewing) {
			isPreviewing = false;
			// surfaceの破棄時にカメラを解放する
			closeCamera();
		}
	}

	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		Log.i(TAG, "surfaceChanged / w:" + w + ", h:" + h);

		if (camera != null) {
			if (isPreviewing) {
				camera.stopPreview();
			}

			// カメラのパラメタ設定
			Camera.Parameters parameters = camera.getParameters();
			parameters.setPreviewSize(w, h);
			parameters.setFocusMode(Parameters.FOCUS_MODE_MACRO);
			//camera.setParameters(parameters);

			// camera.setParameters(parameters);
			// SDK1.5からsetPreviewDisplayはIOExceptionをthrowする
			try {
				camera.setPreviewDisplay(holder);
			} catch (Exception e) {
				e.printStackTrace();
			}

			// カメラプレビュー開始
			camera.startPreview();
			isPreviewing = true;
			requestPreview();
		}

		refreshScreenResolution();
		refreshFrameRect();
	}

	void setFinder(FinderView finder) {
		finderView = finder;
	}

	public void closeCamera() {
		if (camera != null) {
			camera.stopPreview();
			camera.release();
			camera = null;
		}
	}

	/**
	 * ファインダー領域を設定する
	 */
	private void refreshFrameRect() {
		if (finderView != null) {
			finderView.setFramingRect(getFrameRect());
		}
	}

	// ファインダーの領域を生成
	private Rect getFrameRect() {
		Log.i(TAG, "getFramingRect");
		if (rect == null) {
			int size = ((mResolution.x < mResolution.y) ? mResolution.x
					: mResolution.y) * 3 / 4;
			int leftOffset = (mResolution.x - size) / 2;
			int topOffset = (mResolution.y - size) / 2;
			rect = new Rect(leftOffset, topOffset, leftOffset + size, topOffset
					+ size);
		}
		return rect;
	}

	// レイアウトの解像度を取得。
	// Decoderに渡す１次元配列の縦横ピクセル数となる。
	private void refreshScreenResolution() {
		int w = getWidth();
		int h = getHeight();
		Log.i(TAG, "refreshScreenResolution(w:" + w + ", h:" + h);
		mResolution = new Point(w, h);
	}

	// オートフォーカスを起動
	void requestAutoFocus() {
		Log.i(TAG, "requestAutoFocus");
		if (camera != null) {

			camera.autoFocus(autoFocusCallback);
		}
	}

	// プレビュー画像取得
	private void requestPreview() {
		Log.i(TAG, "requestPreview");
		if (camera != null) {
			camera.setPreviewCallback(previewCallback);
		}
	}

	private final Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
		public void onPreviewFrame(byte[] data, Camera camera) {


			Log.i(TAG, "onPreviewFrame");
			camera.setPreviewCallback(null);

			YuvImage yuvimage = new YuvImage(data,ImageFormat.NV21,camera.getParameters().getPreviewSize().width,camera.getParameters().getPreviewSize().height,null);
			//ByteArrayOutputStream baos = new ByteArrayOutputStream();
			//yuvimage.compressToJpeg(new Rect(0,0,camera.getParameters().getPreviewSize().width,camera.getParameters().getPreviewSize().height), 100, baos);

			final int width = camera.getParameters().getPreviewSize().width;// プレビューの幅
	        final int height =camera.getParameters().getPreviewSize().height;  // プレビューの高さ


			Result result = decoder.decode(yuvimage.getYuvData(), width, height,getFrameRect());

			if (result != null) {
				previewCount = 0;
				BarcodeFormat code = result.getBarcodeFormat();
				Log.i(TAG, "preview success(" + code + ", " + result.getText()+ ")");

				// CODE_128形式のバーコードのみ続きの処理を行う
				if (code == BarcodeFormat.CODE_128) {
					mResultDialog.set(result);
					mResultDialog.show();
				} else {
					// CODE_128形式意外のバーコードの場合は
					// 警告ダイアログを表示する
					barCodeActivity.showIncorrectDialog(code.toString());
				}
			} else {
				Log.i(TAG, "preview failed");
				if (previewCount < TRY_PREVIEW) {
					Log.i(TAG, "retry preview");
					previewCount++;
					requestPreview();
				} else {
					Log.i(TAG, "give up preview");
					previewCount = 0;
					// 解析不能なので、再びオートフォーカスで
					// 画像取得しなおす
					requestAutoFocus();
				}
			}
			Log.d(TAG, "onPreviewFrame - wrote bytes: " + data.length);
		}
	};

	// PreviewCallbackとAutoFocusCallbackの相互呼出し
	private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
		public void onAutoFocus(boolean success, Camera camera) {
			Log.i(TAG, "onAutoFocus");
			camera.autoFocus(null);
			requestPreview();
		}
	};

}
